package com.chj.comp.common.security.config;

import cn.hutool.core.util.ReUtil;
import com.chj.comp.common.security.annotation.NoAuth;
import com.chj.comp.common.security.handler.CustomAccessDeniedHandler;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * @Description:
 * @Author: cuihui
 */
@RequiredArgsConstructor
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
@RefreshScope
@ConfigurationProperties(prefix = "security.config", ignoreInvalidFields = true)
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
    private final WebApplicationContext applicationContext;

    /**
     * 资源id
     */
    @Setter
    private String resourceId;

    /**
     * 放行url
     * @Value不能直接给list赋值，使用@ConfigurationProperties，需要给注入的字段添加setter方法
     */
    @Setter
    private List<String> ignoreUrls;

    /**
     *  资源配置
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(resourceId)
                .tokenStore(tokenStore)
                .stateless(true)
                .accessDeniedHandler(new CustomAccessDeniedHandler());
    }

    /**
     * 请求配置
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 获取controller层@NoAuth对应url,并添加至ignoreUrls
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        map.keySet().forEach(info -> {
            HandlerMethod handlerMethod = map.get(info);

            // 获取方法上边的注解 替代path variable 为 *
            NoAuth method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), NoAuth.class);
            Optional.ofNullable(method).ifPresent(noAuth -> info.getPatternsCondition().getPatterns()
                    .forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, "*"))));
        });

        //放行接口（配置文件中读取放行url，此处不再写死）
        String[] ignoreUrlsStr = ignoreUrls.toArray(new String[0]);
        http.authorizeRequests()
                // 开放swagger2访问
                .antMatchers(ignoreUrlsStr).permitAll()
                // 其他均需要认证
                .antMatchers("/**").authenticated()
                // 关闭跨域防护
                .and().csrf().disable();

//        http.authorizeRequests()
//                // 开放swagger2访问
//                .antMatchers("/**/v2/api-docs").permitAll()
//                // 开放健康检查访问
//                .antMatchers("/**/actuator/**").permitAll()
//                // 其他均需要认证
//                .antMatchers("/**").authenticated()
//                // 关闭跨域防护
//                .and().csrf().disable();

//        // 不需要令牌,直接访问资源
//        http.authorizeRequests().anyRequest().permitAll()
//                // 关闭跨域防护
//                .and().csrf().disable();
    }
}
